package com.snk.kof.common.exception.handler;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonParseException;
import com.google.common.base.Throwables;
import com.snk.kof.common.enums.CommonResultEnum;
import com.snk.kof.common.exception.BusinessException;
import com.snk.kof.common.result.Result;
import com.snk.kof.common.utils.HttpContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Collections;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author keshan
 * @date 2022-01-02 下午3:37
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final String SEPARATOR = "; ";

    @ExceptionHandler(BusinessException.class)
    public Result businessExceptionHandler(BusinessException e) {
        log.error(
                "[GlobalExceptionHandler#businessException] message={}, requestURI={}",
                Throwables.getStackTraceAsString(e),
                HttpContextUtil.getHttpServletRequest().getRequestURI()
        );
        return Result.error(e.getCode(), e.getMessage());
    }

    @ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})
    public Result objectValidExceptionHandler(Exception e) {
        String errorMessage = Optional.ofNullable(e)
                .map(be -> (BindException) be)
                .map(BindException::getBindingResult)
                .map(Errors::getFieldErrors)
                .orElse(Collections.emptyList())
                .stream()
                .map(fieldError -> StrUtil.concat(Boolean.TRUE, fieldError.getField(), StrUtil.COLON, fieldError.getDefaultMessage()))
                .collect(Collectors.joining(SEPARATOR));
        log.error(
                "[GlobalExceptionHandler#objectValidExceptionHandler] message={}, requestURI={}",
                Throwables.getStackTraceAsString(e),
                HttpContextUtil.getHttpServletRequest().getRequestURI()
        );
        return Result.error(CommonResultEnum.ERROR_PARAM_VALIDATOR).setMessage(errorMessage);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Result paramValidExceptionHandler(ConstraintViolationException e) {
        String errorMessage = e.getConstraintViolations()
                .stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(SEPARATOR));
        log.error(
                "[GlobalExceptionHandler#paramValidExceptionHandler] message={}, requestURI={}",
                Throwables.getStackTraceAsString(e),
                HttpContextUtil.getHttpServletRequest().getRequestURI()
        );
        return Result.error(CommonResultEnum.ERROR_PARAM_VALIDATOR).setMessage(errorMessage);
    }

    @ExceptionHandler({HttpMessageNotReadableException.class, JsonParseException.class})
    public Result jsonParseExceptionHandler(Exception e) {
        log.error(
                "[GlobalExceptionHandler#jsonParseExceptionHandler] message={}, requestURI={}",
                Throwables.getStackTraceAsString(e),
                HttpContextUtil.getHttpServletRequest().getRequestURI()
        );
        return Result.error(CommonResultEnum.JSON_PARSE_ERROR);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e) {
        log.error(
                "[GlobalExceptionHandler#httpRequestMethodNotSupportedExceptionHandler] message={}, requestURI={}",
                Throwables.getStackTraceAsString(e),
                HttpContextUtil.getHttpServletRequest().getRequestURI()
        );
        return Result.error(CommonResultEnum.HTTP_METHOD_NOT_ALLOWED);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public Result noHandlerFoundExceptionHandler(Exception e) {
        log.error(
                "[GlobalExceptionHandler#noHandlerFoundExceptionHandler] message={}, requestURI={}",
                Throwables.getStackTraceAsString(e),
                HttpContextUtil.getHttpServletRequest().getRequestURI()
        );
        return Result.error(CommonResultEnum.NO_HANDLER_FOUND);
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result httpMediaTypeNotSupportedExceptionHandler(Exception e) {
        log.error(
                "[GlobalExceptionHandler#httpMediaTypeNotSupportedExceptionHandler] message={}, requestURI={}",
                Throwables.getStackTraceAsString(e),
                HttpContextUtil.getHttpServletRequest().getRequestURI()
        );
        return Result.error(CommonResultEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException e) {
        log.error(
                "[GlobalExceptionHandler#missingServletRequestParameterExceptionHandler] message={}, requestURI={}",
                Throwables.getStackTraceAsString(e),
                HttpContextUtil.getHttpServletRequest().getRequestURI()
        );
        return Result.error(CommonResultEnum.ERROR_PARAM_VALIDATOR);
    }

    @ExceptionHandler(Exception.class)
    public Result globalExceptionHandler(Exception e) {
        log.error(
                "[GlobalExceptionHandler#businessException] message={}, requestURI={}",
                Throwables.getStackTraceAsString(e),
                HttpContextUtil.getHttpServletRequest().getRequestURI()
        );
        return Result.error(CommonResultEnum.SYSTEM_ERROR);
    }


}
